#include "Lily_help.h"
// #include <math.h>
#include "hashlib.h"

List_T(Pipe_t) BETA __lily_pipes = NULL;

Pipe BETA li_new_pipe(const char *id, void *arg, PipeLine out)
{
	Pipe p = pipe_find(id);
	if(p)
		return p;
	Pipe_t pip;
	pip.id = id;
	pip.arg = arg;
	pip.out = out;
	pip.next = pip.priv = NULL;
	int i = li_add(__lily_pipes, pip);
	if (i < 0)
		return NULL;
	return list_content(__lily_pipes, Pipe_t) + i;
}
Pipe BETA pipe_find(const char *id)
{
	if (!id)
		return NULL;
	if (!__lily_pipes)
		return NULL;
	Pipe ps = list_content(__lily_pipes, Pipe_t);
	for (int i = 0; i < __lily_pipes->count; i++)
	{
		Pipe pip = &(ps[i]);
		if (pip && pip->id && strcmp(pip->id, id) == 0)
			return pip;
	}
	return NULL;
}
void BETA pipe_remove(Pipe pipe)
{
	if (!pipe)
		return;
	Pipe ps = list_content(__lily_pipes, Pipe_t);
	int index = pipe - ps;
	if (index < 0 || index >= __lily_pipes->count)
		return;
	list_swap_out(__lily_pipes, index);
}
void BETA pipe_remove_tag(const char *id)
{
	if (!id)
		return;
	Pipe pipe = pipe_find(id);
	if (!pipe)
		return;
	pipe_remove(pipe);
}

Pipe li_new_input_device(const char *id)
{
	return li_new_pipe(id, NULL, NULL);
}

Pipe li_new_output_device(const char *id, void *arg, PipeLine stream)
{
	return li_new_pipe(id, arg, stream);
}

Pipe _find_priv_of(Pipe p)
{
	if (!p)
		return NULL;
	Pipe ps = list_content(__lily_pipes, Pipe_t);
	int n = __lily_pipes->count;
	for (int i = 0; i < n; i++)
	{
		if (ps[i].next == p)
			return &(ps[i]);
	}
	return NULL;
}
int32_t pipe_connect_to(Pipe source, Pipe entry)
{
	if (!source || !entry)
		return -1;
	source->next = entry;
	entry->priv = source;
	return 0;
}
int32_t pipe_disconnect(Pipe source)
{
	if (!source)
		return -1;
	source->next->priv = _find_priv_of(source);
	source->next = NULL;
	return 0;
}
int pipe_block(Pipe pipe)
{
	if (!pipe)
		return -1;
	Pipe ps = list_content(__lily_pipes, Pipe_t);
	int n = __lily_pipes->count;
	for (int i = 0; i < n; i++)
	{
		if (ps[i].next == pipe)
		{
			ps[i].next = NULL;
		}
	}
	pipe->priv = NULL;
	return 0;
}

void *pipe_release_data(Pipe pipe, char *data, int32_t len)
{
	if (!pipe || !pipe->next || !pipe->next->out)
		return NULL;
	return pipe->next->out(pipe->next->arg, data, len);
}

void pipe_show_connection()
{
	if (!__lily_pipes)
		return;
	Pipe pipes = list_content(__lily_pipes, Pipe_t);
	for (int i = 0; i < __lily_pipes->count; i++)
	{
		Pipe p = &(pipes[i]);
		if (p->priv && p->next)
			lily_outf("%s>%s>%s\n", p->priv->id, p->id, p->next->id);
		else if (p->priv)
			lily_outf("%s>%s\n", p->priv->id, p->id);
		else if (p->next)
			lily_outf("%s>%s\n", p->id, p->next->id);
		else
		{
			lily_out(p->id);
			lily_out("\n");
		}
	}
}

const char *cmd_pipe_annotation = "pipeLine control:\n"
								  " list|find [pipe] :show available pipes\n"
								  " block pipe: block the pipe, free from any inputting\n"
								  " connect [source] [endpoint]:show connections, build or disconnect pipes";
int32_t cmd_pipe(int32_t n, char *arg[])
{
	assert_true(__lily_pipes);
	if (n == 1 || (n == 2 && arg[1][0] == 'l')) // list all
	{
		int32_t i;
		Pipe pipes = list_content(__lily_pipes, Pipe_t);
		for (i = 0; i < __lily_pipes->count; i++)
		{
			lily_outf("%s\n", pipes[i].id);
		}
		return 0;
	}
	// n>1
	if (arg[1][0] == 'l' || arg[1][0] == 'f') // n>2
	{
		Pipe p = pipe_find(arg[2]);
		assert_msg(p, "pipe not found");
		if (p->priv)
			lily_outf("%s>", p->priv->id);
		lily_out(p->id);
		if (p->next)
			lily_outf(">%s", p->next->id);
		lily_out("\n");
		return 0;
	}
	if (arg[1][0] == 'b')
	{
		assert_msg(n > 2, "bad arguments");
		Pipe p = pipe_find(arg[2]);
		assert_msg(p, "pipe not found");
		pipe_block(p);
		return 0;
	}
	if (arg[1][0] == 'c')
	{
		if (n == 2) // show connection
		{
			pipe_show_connection();
		}
		else if (n == 3) // connect keyboard, disconnect
		{
			Pipe src = pipe_find(arg[2]);
			assert_msg(src, "pipe not found");
			pipe_disconnect(src);
			return 0;
		}
		else if (n == 4) // connect keyboard screen
		{
			Pipe src = pipe_find(arg[2]);
			assert_msg(src, "pipe not found");
			Pipe ety = pipe_find(arg[3]);
			assert_msg(ety, "pipe not found");
			pipe_connect_to(src, ety);
			return 0;
		}
		return 0;
	}
	li_error("bad arguments", -1);
}

static void _on_memory_reduction(void *private_data, void *arg)
{
	if (__lily_pipes)
		list_reduction(__lily_pipes);
}
void pipe_init()
{
	__lily_pipes = new_li(Pipe_t);
	public_a_cmd_link_doc("pipe", cmd_pipe, cmd_pipe_annotation);
	event_register_to_tag("memReduction",NULL,_on_memory_reduction);
}